﻿using Acornima.Ast;
using Jint;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using T.FAS.Runtime.Action.Service;
using T.FAS.Runtime.Action.SPI;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.Base.Service;
using T.FAS.Runtime.Expression.Entity;
using T.FAS.Runtime.Expression.Service;
using T.FAS.Runtime.Metadata.Client;
using T.FAS.Runtime.Metadata.Entity;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.Metadata.Entity.Page;
using T.FAS.Runtime.ScheduleJob.SPI;

namespace T.FAS.Runtime.ScheduleJob.SubscriptionJobEngine
{
    public class SubscriptionJobExecuteEngine : IScheduleJobExecutor
    {
        private TResult BuildScheduleJobDataResult(ScheduleJobSubscription subscription, object dataResult)
        {
            if (subscription.IsBasic)
            {
                if (subscription.ReturnType == DataType.List)
                {
                    var tResult = new TCommonResult<TRecord>()
                    {
                        ActionCode = subscription.ReturnDataType,
                        ActionName = subscription.ReturnDataType,
                        ResultType = DataType.List,
                        DataContent = dataResult
                    };
                    var dataList = dataResult as object[];
                    foreach (var item in dataList)
                    {
                        if (tResult.List == null)
                        {
                            tResult.Schema = BasicTypeData.BuildSchema(subscription.ReturnDataType);
                            tResult.List = new TArray<TRecord>(new TEntity());
                        }
                        tResult.List.Add(new TRecord(new Dictionary<string, object>() { { BasicTypeData.DefaultPropertyName, item } }));
                    }
                    tResult.Record = new TRecord();
                    return tResult;
                }
            }
            else
            {
                if (subscription.ReturnType == DataType.List)
                {
                    var expResult = (dataResult as ExprResult).Result;
                    var tResult = new TCommonResult<TRecord>()
                    {
                        ActionCode = subscription.ReturnDataType,
                        ActionName = subscription.ReturnDataType,
                        ResultType = DataType.List,
                        DataContent = expResult
                    };
                    tResult.Schema = new TSchema()
                    {
                        Entity = new TEntity()
                    };

                    var list = JsonConvert.DeserializeObject<List<JObject>>(JsonConvert.SerializeObject(expResult));
                    foreach (var item in list)
                    {
                        tResult.List.Add(TRecord.BuildRecord(item as JObject));
                    }
                    tResult.Record = new TRecord();
                    return tResult;
                }
            }


            var result = false;
            var dataResultString = Convert.ToString(dataResult);
            if (dataResultString.Equals("true", StringComparison.OrdinalIgnoreCase) || dataResultString.Equals("1"))
                result = true;

            var jobResult = new TCommonResult<TRecord>()
            {
                ActionCode = "ScheduleJobSubscription",
                ActionName = "ScheduleJobSubscription",
                Count = 0,
                ResultType = DataType.Record,
                DataContent = result,
            };

            jobResult.Record = new TRecord();
            jobResult.Record.SetValue(BasicTypeData.DefaultPropertyName, result);
            return jobResult;
        }

        public object Execute(ScheduleJobExecContext context)
        {
            var subscriptionContext = context.Instance.Data.GetJsonData();
            if (string.IsNullOrEmpty(subscriptionContext))
                throw new Exception($"订阅信息不存在");
            var subscription = JsonConvert.DeserializeObject<ScheduleJobSubscription>(subscriptionContext);
            var dataResult = GetTResult(subscription.SourceID);
            dynamic dataCollection = dataResult;
            int count = Math.Max(dataCollection?.Count, dataCollection?.List?.Count);
            Base.Common.LogUtil.Log($"{subscription.SourceID},{subscription.OwnerName},{count}", "ScheduleJobLog");
            if (CheckIsMeetTriggerCondition(subscription, dataResult))
            {
                subscription.NoticeSets.ForEach(n =>
                {
                    var notice = new NoticeImplProvider().CreateNoticeHandler(n);
                    if (notice != null)
                        notice.Execute(subscription, dataResult, n);
                });
            }
            return dataResult;
        }
        /// <summary>
        /// 处理查询方案并获取结果
        /// </summary>
        /// <param name="planId"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public TResult GetTResult(string planId)
        {
            var clientService = MetaDataClientService.GetInstance();
            List<ActionParameter> actionParameters = new List<ActionParameter>();
            QueryPlan queryPlan = new QueryPlanService().GetQueryPlanV2(planId);
            if (queryPlan == null)
                throw new Exception($"{planId}查询方案不存在");
            PageMetaData pageMetaData = clientService.GetMetaData(queryPlan.PageID)?.Content as PageMetaData;
            if (pageMetaData == null)
                throw new Exception($"{queryPlan.PageID}界面不存在");
            bool handlePlan = false;
            foreach (var item in pageMetaData.PCUIDom.Controls)
            {
                InitParam(actionParameters, item, queryPlan, handlePlan);
            }
            var metadata = clientService.GetMetaDataContent(queryPlan.DQID);
            TResult tResult;
            if (metadata is ActionMetaData)
            {
                tResult = ActionExecuteService.GetInstance.Execute(queryPlan.DQID, Action.SPI.ActionType.Action, actionParameters, false);
            }
            else if (metadata is DataQuery)
            {
                tResult = ActionExecuteService.GetInstance.Execute(queryPlan.DQID, Action.SPI.ActionType.DQ, actionParameters, false);
            }
            else
            {
                throw new Exception($"{queryPlan.DQID}查询元数据不存在");
            }
            return tResult;
        }

        public void InitParam(List<ActionParameter> actionParameters, PageUIControl pageUIControl, QueryPlan queryPlan, bool handlePlan)
        {
            if (!handlePlan && (pageUIControl.CtrlType == "fas-advquery-1_1" || pageUIControl.CtrlType == "fasr-advfilter-p" || pageUIControl.CtrlType == "fasr-advfilter-m"))
            {
                var options = JsonConvert.DeserializeObject<Dictionary<string, object>>(pageUIControl.Options);
                if (options?.ContainsKey("dataQuery") == true)
                {
                    var dataQuery = JsonConvert.DeserializeObject<Dictionary<string, object>>(JsonConvert.SerializeObject(options["dataQuery"]));

                    if (dataQuery?.ContainsKey("Parameters") == true)
                    {
                        handlePlan = true;
                        var parameters = JsonConvert.DeserializeObject<List<ViewModelParameter>>(JsonConvert.SerializeObject(dataQuery["Parameters"]));
                        foreach (var item in parameters)
                        {
                            switch (item.Code)
                            {
                                case "PageIndex":
                                case "FasPageIndex":
                                    actionParameters.Add(new ActionParameter { ParameterName = item.Code, ParameterValue = 0 });
                                    break;
                                case "PageSize":
                                case "FasPageCount":
                                    actionParameters.Add(new ActionParameter { ParameterName = item.Code, ParameterValue = 0 });
                                    break;
                                case "AdvancedSort":
                                case "FasDynamicSort":
                                    actionParameters.Add(new ActionParameter { ParameterName = item.Code, ParameterValue = queryPlan.SortContent });
                                    break;
                                case "AdvancedFilter":
                                    if (queryPlan.FilterContent.Contains("@"))
                                    {
                                        Group group = JsonConvert.DeserializeObject<Group>(queryPlan.FilterContent);
                                        ExprService.GetInstance.TransformGroup(group, queryPlan.UserID, queryPlan.UserName);
                                        actionParameters.Add(new ActionParameter { ParameterName = item.Code, ParameterValue = JsonConvert.SerializeObject(group) });
                                    }
                                    else
                                    {
                                        actionParameters.Add(new ActionParameter { ParameterName = item.Code, ParameterValue = queryPlan.FilterContent });
                                    }
                                    break;
                                case "FasDynamicFilter":
                                    Group group2 = JsonConvert.DeserializeObject<Group>(queryPlan.FilterContent);
                                    if (queryPlan.FilterContent.Contains("@"))
                                    {
                                        ExprService.GetInstance.TransformGroup(group2, queryPlan.UserID, queryPlan.UserName);
                                    }
                                    actionParameters.Add(new ActionParameter { ParameterName = item.Code, ParameterValue = GetGroupExpr(group2) });
                                    break;
                                default:
                                    if (!string.IsNullOrEmpty(item.Value))
                                    {
                                        var script = Engine.PrepareScript(item.Value);
                                        NodeList<Statement> statements = script.Program.Body;
                                        if (statements.Count > 0 && statements[0] is ExpressionStatement statement)
                                        {
                                            if (statement.Expression is Literal literal)
                                            {
                                                actionParameters.Add(new ActionParameter { ParameterName = item.Code, ParameterValue = literal.Value });
                                            }
                                        }
                                    }
                                    break;
                            }
                        }
                    }
                }
            }
            else if (pageUIControl.Children.Count > 0)
            {
                foreach (var item in pageUIControl.Children)
                {
                    InitParam(actionParameters, item, queryPlan, handlePlan);
                }
            }
        }
        /// <summary>
        /// 根据高级查询获取可执行表达式
        /// </summary>
        /// <param name = "group"></param>
        /// <returns></returns>
        private Dictionary<string, object> GetGroupExpr(Group group)
        {
            Dictionary<string, object> dic = new Dictionary<string, object>();
            if (group.Filters != null && group.Filters.Count > 0)
            {
                foreach (var item in group.Filters)
                {
                    if (item is Filter filter)
                    {
                        string[] codes = filter.Code.Split('.');
                        string code;
                        if (codes.Length == 0)
                            code = codes[0];
                        else
                            code = codes[1];
                        if (string.IsNullOrEmpty(code))
                            continue;
                        if (filter.Value.Count > 1)
                        {
                            dic.Add(code, JsonConvert.SerializeObject(filter.Value));
                        }
                        else
                        {
                            dic.Add(code, filter.Value[0]);
                        }
                    }
                }
            }
            return dic;
        }
        /// <summary>
        /// 检查是否满足通知条件
        /// </summary>
        /// <param name="subscription"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        private bool CheckIsMeetTriggerCondition(ScheduleJobSubscription subscription, TResult result)
        {
            dynamic dataCollection = result;
            int count = Math.Max(dataCollection?.Count, dataCollection?.List?.Count);

            switch (subscription.TriggerType)
            {
                case TriggerType.HasData:
                    {
                        if (count > 0)
                            return true;
                    }
                    break;
                case TriggerType.NoData:
                    {
                        if (count <= 0)
                            return true;
                    }
                    break;
                case TriggerType.Condition:
                    {
                        var condition = subscription.ComplexTriggerConditionView;
                        if (condition != null)
                        {
                            var compareValue = condition.CompareValue;
                            switch (condition.CompareOperation)
                            {
                                case CompareOperation.Equal:
                                    {
                                        if (compareValue.Equals(0))
                                        {
                                            if (count <= 0)
                                                return true;
                                        }
                                        else
                                        {
                                            if (count == compareValue)
                                                return true;
                                        }
                                    }
                                    break;
                                case CompareOperation.LessThan:
                                    {
                                        if (count < compareValue)
                                            return true;
                                    }
                                    break;
                                case CompareOperation.MoreThan:
                                    {
                                        if (count > compareValue)
                                            return true;
                                    }
                                    break;
                                case CompareOperation.NoLessThan:
                                    {
                                        if (count >= compareValue)
                                            return true;
                                    }
                                    break;
                                case CompareOperation.NoMoreThan:
                                    {
                                        if (compareValue.Equals(0))
                                        {
                                            if (count <= 0)
                                                return true;
                                        }
                                        else
                                        {
                                            if (count <= compareValue)
                                                return true;
                                        }
                                    }
                                    break;
                                case CompareOperation.NotEqual:
                                    {
                                        if (!compareValue.Equals(0))
                                        {
                                            if (dataCollection.List == null)
                                            {
                                                return true;
                                            }
                                            else if (count != compareValue)
                                            {
                                                return true;
                                            }
                                        }
                                        else
                                        {
                                            if (count > 0)
                                                return true;
                                        }
                                    }
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                    break;
                default:
                    break;
            }
            return false;
        }
    }
}
